#include "BinarySVM.h"
#include <math.h>
//#include <stdlib.h>     /* srand, rand */
#include <time.h>       /* time */
#ifdef WIN32
#include "MemoryOperators.h"
#endif
//#include <stdio.h>
void BinarySVM::InitData(CTrainVector *arrayVec1, int numOfVec1, CTrainVector *arrayVec2, int numOfVec2)
{
	m_ArrayVec1 = arrayVec1;
	m_numOfVec1 = numOfVec1;
	m_ArrayVec2 = arrayVec2;
	m_numOfVec2 = numOfVec2;
	m_NumOfVectors = numOfVec1 + numOfVec2;
	/*
	m_Targets = new int [m_NumOfVectors];
	int i;
	for (i = 0; i < m_NumOfVectors; ++i)
	{
		m_Targets[i] =  i < m_numOfVec1 ? 1 : -1;		
	}*/
}


void BinarySVM::Init(CTrainVector *arrayVec1, int numOfVec1, CTrainVector *arrayVec2, int numOfVec2, double sigma, double C )
{
	InitData(arrayVec1, numOfVec1, arrayVec2, numOfVec2);
	m_sigma = sigma;
	m_C = C;
	
	
}

double BinarySVM::kernel_func(int i1, int i2)
{
	if (i1 == i2) return 1;
	double d = 0, tmp;
	int i;
	CTrainVector *pt1, *pt2;
	if(i1 < m_numOfVec1)
	{
		pt1 = m_ArrayVec1;
	}
	else
	{
		pt1 = m_ArrayVec2;
		i1 = i1 - m_numOfVec1;
	}
	
	if(i2 < m_numOfVec1)
	{
		pt2 = m_ArrayVec1;
	}
	else
	{
		pt2 = m_ArrayVec2;
		i2 = i2 - m_numOfVec1;
	}
	
	for (i = 0; i < m_ArrayVec1[i1].m_LenOfArray; ++i)
	{
		tmp = pt1[i1].m_arrayFloat[i] - pt2[i2].m_arrayFloat[i];
		d += tmp * tmp;
	}
	d = -d/ (2.0 * m_sigma * m_sigma);
	d = exp (d);
	return d;// kernel gauss
}
double BinarySVM::kernel_func(int i, float *xVec, int n)
{
	CTrainVector *pt;
	if(i < m_numOfVec1)
	{
		pt = m_ArrayVec1;
	}
	else
	{
		pt = m_ArrayVec2;
		i = i - m_numOfVec1;
	}
	if (pt[i].m_LenOfArray != n)
		return 0;
	double d = 0, tmp;
	int j;
	for (j = 0; j < pt[i].m_LenOfArray; ++j)
	{
		tmp = pt[i].m_arrayFloat[j] - xVec[j];
		d += tmp * tmp;
	}
	d = -d/ (2.0 * m_sigma * m_sigma);
	d = exp (d);
	return d;// kernel gauss
}

void BinarySVM::SMOTraining()
{
	printf ( "train data\n");
	m_Coefs = new double[m_NumOfVectors];
	m_Targets = new int [m_NumOfVectors];
	m_SVMOutputs = new double [m_NumOfVectors];
	int i;
	for (i = 0; i < m_NumOfVectors; ++i)
	{
		m_Coefs[i] = 0.0;
		m_SVMOutputs[i] = 0.0;
		m_Targets[i] =  i < m_numOfVec1 ? 1 : -1;
			
	}
	// m_sigma = sigma;
	// m_C = C;
	Threshold = 0.001;
	int numChanged = 0;
	int examineAll = 1;
	int j;
	m_NumOfNoneOC = 0;
	bValue = 0;
	while( (numChanged > 0) || (examineAll) )
	{
		numChanged = 0;
		if (examineAll)
		{
			for(j = 0; j < m_NumOfVectors; ++j)
			{
				numChanged += examineExample(j);
			}
		}
		else
		{
			for (j = 0; j < m_NumOfVectors; ++j)
			{
				if ((m_Coefs[j] > 0) && (m_Coefs[j] < m_C))
				{
					numChanged += examineExample(j);
				}
			}
		}
		if (examineAll)
		{
			examineAll = 0;
		}
		else if (numChanged == 0)
		{
			examineAll = 1;
		}
	}
	//finish training
}

int BinarySVM::examineExample(int i2)
{
	int i1, r, i1max;
	double E1max, z;
	
	double E2, r2, u2;
	int y2;
	y2 = m_Targets[i2];
	u2 = m_Coefs[i2];
	
	E2 = m_SVMOutputs[i2] - y2; // check in error cache
	r2 = E2 * y2;
	srand (time(NULL));
	if ( ((r2 < -Threshold) && (u2 < m_C)) || ((r2 > Threshold) && (u2 > 0)) )
	{
		if ( m_NumOfNoneOC > 1)
		{
			//chose heuristic, chose i1 if dist( svm cal to target )is max
			E1max = 0;
			for (i1 = 0; i1 < m_NumOfVectors; ++i1)
			{
				z = m_SVMOutputs[i1] - m_Targets[i1];
				z = E2 - z;
				if (z < 0) z = -z;
				if (E1max < z)
				{
					E1max = z;
					i1max = i1;
				}
			}
			i1 = i1max;
			// end heuristic
			
			if (takeStep(i1, i2)) return 1;
		}
		
		r = rand() % m_NumOfVectors;
		for (i1 = r; i1 < m_NumOfVectors; ++i1)
		{
			if (i1 != i2)
			{
				if ( (m_Coefs[i1] > 0) && (m_Coefs[i1] < m_C) )
				{
						if (takeStep(i1, i2)) return 1;
				}
			}
		}
		/*
		for (i1 = 0; i1 < r; ++i1)
		{
			if (i1 != i2)
			{
				if ( (m_Coefs[i1] > 0) && (m_Coefs[i1] < m_C) )
				{
					if (takeStep(i1, i2) return 1;
				}
			}
		}*/
			
		r = rand() % m_NumOfVectors;
		for (i1 = r; i1 < m_NumOfVectors; ++i1)
		{
			if (i1 != i2)
			{
				if (takeStep(i1, i2)) return 1;
			}
		}
		/*
		for (i1 = 0; i1 < r; ++i1)
		{
			if (i1 != i2)
			{
					if (takeStep(i1, i2) return 1;
			}
		}*/
	}
	
	return 0;
}

int BinarySVM::takeStep(int i1, int i2)
{
	int y1, y2, j, s;
	double u1, u2, E1, E2, L, H, gamma, k11, k12, k22, eta;
	double u1new, u2new, bOld, b1, b2, k1j, k2j;
	
	u1 = m_Coefs[i1];
	y1 = m_Targets[i1];
	E1 = m_SVMOutputs[i1] - y1;
	
	u2 = m_Coefs[i2];
	y2 = m_Targets[i2];
	E2 = m_SVMOutputs[i2] - y2;
	
	s = y1 * y2;
	
	if (y1 != y2)
	{
		gamma = u2 - u1;
		if (gamma < 0)
		{
			L = gamma;
			H = m_C;
		}
		else
		{
			L = 0;
			H = m_C + gamma;
		}
	}
	else
	{
		gamma = u2 + u1;
		if (gamma > m_C)
		{
			L = gamma - m_C;
			H = m_C;
		}
		else
		{
			L = 0;
			H = gamma;
		}
	}
	
	if ( L == H ) return  0;
	
	k11 = kernel_func(i1, i1);
	k12 = kernel_func(i1, i2);
	k22 = kernel_func(i2, i2);
	eta = 2 * k12 - k11 - k22;
	
	double eps = 0.0001;
	if (eta < 0)
	{
		u2new = u2 + y2 * (E2 - E1) / eta;
		
		if (u2new < L)
		{
			u2new = L;
		}
		else if (u2new > H)
		{
			u2new = H;
		}
				
	}
	else
	{
		/*
		double f1, f2, L1, H1, Lobj, Hobj;
		f1 = y1 * E1 - u1 * k11 - s * u2 * k12;
		f2 = y2 * E2 - u2 * k22 - s * u1 * k12;
		L1 = u1 + s * (u2 - L);
		H1 = u1 + s * (u2 - H);
		Lobj = -(L1 * f1 + L * f2 + L1 * L1 * k11 * 0.5 + L * L * k22 * 0.5 + s * L * L1 * k12);
		Hobj = -(H1 * f1 + H * f2 + H1 * H1 * k11 * 0.5 + H * H * k22 * 0.5 + s * H * H1 * k12);
		*/
		
		// note: this case eta could be zero, ref "A Practical SMO Algorithm" by J.
        // Dong, A. Krzyzak and C. Y. Suen.
		double Lobj = y2 * (E1 - E2) * L;
        double Hobj = y2 * (E1 - E2) * H;
		
		if (Lobj > Hobj+eps)
			u2new = L;
		else if (Lobj < Hobj-eps)
			u2new = H;
		else
			u2new = u2;
	}
	if (abs(u2new-u2) < eps*(u2new+u2+eps))
		return 0;
	u1new = u1 + s * (u2 - u2new);
	
	if (u1new < 0) 
	{
		u2new += s * u1new;
		u1new = 0;
	}
	else if (u1new > m_C) 
	{
		double t = u1new - m_C;
		u2new += s * t;
		u1new = m_C;
	}
	
	bOld = bValue;
	
	b1 = bOld -E1 - (u1new - u1) * y1 * k11 - (u2new - u2) * y2 * k12;
	b2 = bOld - E1 - (u1new - u1) * y1 * k12 - (u2new - u2) * y2 * k22;
	bValue = (b1 + b2) /2;
	
	for(j = 0; j < m_NumOfVectors; ++j)
	{
		k1j = kernel_func(i1, j);
		k2j = kernel_func(i2, j);
		
		m_SVMOutputs[j] += (u1new - u1) * y1 * k1j + (u2new - u2) * y2 * k2j + bValue - bOld;
	}
	
	m_Coefs[i1] = u1new;
	m_Coefs[i2] = u2new;
	
	if ( (u1 == 0) || ( u1 == m_C) )
	{
		if ( (u1new > 0) && (u1new < m_C) ) 
		{
			++m_NumOfNoneOC;
		}
	}
	else if ( (u1new == 0) && (u1new == m_C) ) 
	{
		--m_NumOfNoneOC; 
	}
	
	if ( (u2 == 0) || ( u2 == m_C) )
	{
		if ( (u2new > 0) && (u2new < m_C) ) 
		{
			++m_NumOfNoneOC;
		}
	}
	else if ( (u2new == 0) && (u2new == m_C) ) 
	{
		--m_NumOfNoneOC; 
	}
	
	return 1;
}

int BinarySVM::classify(float *xVec, int n)
{
	int i;
	double sum = bValue;
	
	for (i = 0; i < m_NumOfVectors; ++i)
	{
		printf("duong debug %d\n", i);
		//assert(m_Coefs[i]);
	//assert(m_Targets[i]);
		sum += m_Coefs[i] * m_Targets[i] * kernel_func(i, xVec, n);
	}
	return sum > 0 ? 1 : 0;
}

void BinarySVM::SaveToFilePointer(FILE *pFile)
{
	// FILE * pFile;
	//pFile = fopen ( strOutPath , "wb" );
	fwrite (&m_sigma , sizeof(m_sigma), 1, pFile);
	fwrite (&bValue , sizeof(bValue), 1, pFile);
	fwrite (&m_NumOfVectors , sizeof(m_NumOfVectors), 1, pFile);
	
	fwrite (m_Coefs , sizeof(double), m_NumOfVectors, pFile);
	fwrite (m_Targets , sizeof(int), m_NumOfVectors, pFile);
	
	//fclose (pFile);
}

void BinarySVM::LoadFormFilePointer(FILE *pFile)
{
	// FILE * pFile;
	//pFile = fopen ( strOutPath , "wb" );
	fread (&m_sigma , sizeof(m_sigma), 1, pFile);
	fread (&bValue , sizeof(bValue), 1, pFile);
	fread (&m_NumOfVectors , sizeof(m_NumOfVectors), 1, pFile);
	m_Coefs = new double[m_NumOfVectors];
	m_Targets = new int [m_NumOfVectors];
	fread (m_Coefs , sizeof(double), m_NumOfVectors, pFile);
	fread (m_Targets , sizeof(int), m_NumOfVectors, pFile);
	
	//fclose (pFile);
}

void BinarySVM::FreeMemory()
{
	if (m_Coefs)
	{
		delete [] m_Coefs;
	}
	if (m_Targets)
	{
		delete [] m_Targets;
	}
	if (m_SVMOutputs)
	{
		delete [] m_SVMOutputs;
	}
}